//
// Created by DELL on 2023/4/27.
//
#include "quickjspp.hpp"
#include <iostream>

class MyClass
{
public:
    MyClass() {}
    MyClass(std::vector<int>) {}

    double member_variable = 5.5;
    std::string member_function(const std::string& s)
    {
        return "Hello, " + s + " , variable=" + std::to_string(this->member_variable);
    }
};

void println(qjs::rest<std::string> args)
{
    for (auto const& arg : args)
    {
        std::cout << arg << " ";
    }
    std::cout << std::endl;
}

int main()
{
    auto p = &MyClass::member_function;
    auto p2 = &MyClass::member_variable;
    MyClass myClass;
    myClass.member_variable;
    (myClass.*p2) = 5;
    std::cout << "My Class: " << (myClass.*p)("") << std::endl;

    qjs::Runtime runtime;
    qjs::Context context(runtime);
    try
    {
        // export classes as a module
        auto& module = context.addModule("MyModule");
        module.function<&println>("println");
        module.class_<MyClass>("MyClass")
            .constructor<>()
            .constructor<std::vector<int>>("MyClassA")
            .fun<&MyClass::member_variable>("member_variable")
            .fun<&MyClass::member_function>("member_function");
        // import module
        context.eval(R"xxx(
            import * as my from 'MyModule';
            globalThis.my = my;
        )xxx",
                     "<import>", JS_EVAL_TYPE_MODULE);
        // evaluate js code
        context.eval(R"xxx(
            let v1 = new my.MyClass();
            v1.member_variable = 1;
            let v2 = new my.MyClassA([1,2,3]);
            function my_callback(str) {
              my.println("at callback:", v2.member_function(str));
            }
        )xxx");

        // callback
        auto cb = (std::function<void(const std::string&)>) context.eval("my_callback");
        cb("world");
    }
    catch (qjs::exception)
    {
        auto exc = context.getException();
        std::cerr << (std::string) exc << std::endl;
        if ((bool) exc["stack"])
            std::cerr << (std::string) exc["stack"] << std::endl;
        return 1;
    }
}